#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#pragma pack(1)
typedef struct tagBITMAPFILEHEADER{
    unsigned short bfType;
    unsigned long bfSize;
    unsigned short bfReserved1;
    unsigned short bfReserved2;
    unsigned long bfOffBits;
}BITMAP_FILE_HEADER;

typedef struct tagBITMAPINFOHEADER{
    unsigned long biSize;
    unsigned long biWidth;
    unsigned long biHeight;
    unsigned short biPlanes;
    unsigned short biBitCount;
    unsigned long biCompression;
    unsigned long biSizeImage;
    unsigned long biXPelsPerMeter;
    unsigned long biYPelsPerMeter;
    unsigned long biClrUsed;
    unsigned long biClrImportant;
}BITMAP_INFO_HEADER;

unsigned char* readBMP(const char* filename,BITMAP_FILE_HEADER* fileHeader,BITMAP_INFO_HEADER* infoHeader){
    FILE* file=fopen(filename,"rb");
    if(!file){
        printf("无法打开文件\n");
        return NULL;
    }
    fread(fileHeader,sizeof(BITMAP_FILE_HEADER),1,file);
    fread(infoHeader,sizeof(BITMAP_INFO_HEADER),1,file);
    if(infoHeader->biBitCount!=24){
        printf("错误：非24位BMP文件\n");
        fclose(file);
        return NULL;
    }
    int rowSize=(infoHeader->biWidth*3+3)/4*4;
    int dataSize=rowSize*infoHeader->biHeight;
    unsigned char* data=(unsigned char*)malloc(dataSize);
    fseek(file,fileHeader->bfOffBits,SEEK_SET);
    fread(data,1,dataSize,file);
    fclose(file);
    return data;
}

void getImageMinMax(unsigned char* grayData,BITMAP_INFO_HEADER infoHeader,unsigned char* minVal,unsigned char* maxVal) {
    *minVal=255;
    *maxVal=0;
    int pixelCount=infoHeader.biWidth*infoHeader.biHeight;
    for (int i=0;i<pixelCount;i++){
        if(grayData[i]<*minVal)*minVal=grayData[i];
        if(grayData[i]>*maxVal)*maxVal=grayData[i];
    }
}

void grayTransform(unsigned char* data,BITMAP_INFO_HEADER infoHeader,int method,double a_prime,double b_prime)
{
    int rowSize=(infoHeader.biWidth*3+3)/4*4;
    unsigned char* grayData=(unsigned char*)malloc(infoHeader.biWidth*infoHeader.biHeight);
    if(!grayData){
        printf("错误：内存分配失败\n");
        return;
    }
    for(int y=0;y<infoHeader.biHeight;y++){
        for(int x=0;x<infoHeader.biWidth;x++){
            int idx=y*rowSize+x*3;
            unsigned char r=data[idx];
            unsigned char g=data[idx+1];
            unsigned char b=data[idx+2];
            grayData[y*infoHeader.biWidth+x]=(unsigned char)(0.299*r+0.587*g+0.114*b);
        }
    }
    if(method==1){
        unsigned char a_actual,b_actual;
        getImageMinMax(grayData,infoHeader,&a_actual,&b_actual);
        if(a_actual==b_actual){
            unsigned char mid_value=(unsigned char)((a_prime+b_prime)/2);
            memset(grayData,mid_value,infoHeader.biWidth*infoHeader.biHeight);
            printf("原图所有像素值相同，设为中间值%d\n",mid_value);
        }
        else{
            double scale=(b_prime-a_prime)/(b_actual-a_actual);
            for(int y=0;y<infoHeader.biHeight;y++){
                for(int x=0;x<infoHeader.biWidth;x++){
                    int i=y*infoHeader.biWidth+x;
                    double newValue=a_prime+scale*(grayData[i]-a_actual);
                    if(newValue<0)newValue=0;
                    if(newValue>255)newValue=255;
                    grayData[i]=(unsigned char)newValue;
                }
            }
        }
    }
    else if(method==2){
        unsigned char min_val,max_val;
        getImageMinMax(grayData,infoHeader,&min_val,&max_val);
        
        if(a_prime<0)a_prime=0;
        if(b_prime>255)b_prime=255;
        
        if(a_prime>=b_prime||min_val==max_val){
            printf("对数变换参数无效，使用默认值\n");
            a_prime=0;
            b_prime=255;
        }
        double c=log(1+max_val)/(b_prime-a_prime);
        for(int y=0;y<infoHeader.biHeight;y++){
            for(int x=0;x<infoHeader.biWidth;x++){
                int i=y*infoHeader.biWidth+x;
                double newValue=a_prime+log(1+grayData[i])/c;
                if(newValue<0)newValue=0;
                if(newValue>255)newValue=255;
                grayData[i]=(unsigned char)newValue;
            }
        }
    }
    for(int y=0;y<infoHeader.biHeight;y++){
        for(int x=0;x<infoHeader.biWidth;x++){
            int srcIdx=y*rowSize+x*3;
            unsigned char gray=grayData[y*infoHeader.biWidth+x];
            data[srcIdx]=gray;
            data[srcIdx+1]=gray;
            data[srcIdx+2]=gray;
        }
    }
    free(grayData);
}

void histogramEqualization(unsigned char* data,BITMAP_INFO_HEADER infoHeader){
    int width=infoHeader.biWidth;
    int height=infoHeader.biHeight;
    int rowSize=(width*3+3)/4*4;
    int pixelCount=width*height;
    unsigned char* grayData=(unsigned char*)malloc(pixelCount);
    for(int y=0;y<height;y++)
        for(int x=0;x<width;x++)
            grayData[y*width+x]=data[y*rowSize+x*3+1];
    int hist[256]={0};
    for(int i=0;i<pixelCount;i++)
        hist[grayData[i]]++;
    double cdf[256]={0.0};
    cdf[0]=(double)hist[0]/pixelCount;
    for(int i=1;i<256;i++)
        cdf[i]=cdf[i-1]+(double)hist[i]/pixelCount;
    unsigned char mapping[256];
    int lastMapped=0;
    for(int i=0;i<256;i++){
        int mapped=(int)(cdf[i]*255+0.5);
        if(mapped<lastMapped)mapped=lastMapped;
        if(mapped>255)mapped=255;
        mapping[i]=(unsigned char)mapped;
        lastMapped=mapped;
    }
    for(int y=0;y<height;y++){
        for(int x=0;x<width;x++){
            int idx=y*width+x;
            unsigned char gray=mapping[grayData[idx]];
            int srcIdx=y*rowSize+x*3;
            data[srcIdx]=gray;
            data[srcIdx+1]=gray;
            data[srcIdx+2]=gray;
        }
    }
    free(grayData);
}

void imageSmoothing(unsigned char* data,BITMAP_INFO_HEADER infoHeader,int windowSize){
    int width=infoHeader.biWidth;
    int height=infoHeader.biHeight;
    int rowSize=(width*3+3)/4*4;
    unsigned char* temp=(unsigned char*)malloc(rowSize*height);
    if(!temp){
        printf("内存分配失败\n");
        return;
    }
    memcpy(temp,data,rowSize*height);
    int halfSize=windowSize/2;
    double weight=1.0/(windowSize*windowSize);
    for(int y=0;y<height;y++){
        for(int x=0;x<width;x++){
            double bSum=0,gSum=0,rSum=0;
            int validCount=0;
            for(int dy=-halfSize;dy<=halfSize;dy++){
                for(int dx=-halfSize;dx<=halfSize;dx++){
                    int nx=x+dx;
                    int ny=y+dy;
                    if(nx>=0&&nx<width&&ny>=0&&ny<height){
                        int srcIdx=ny*rowSize+nx*3;
                        bSum+=temp[srcIdx];
                        gSum+=temp[srcIdx+1];
                        rSum+=temp[srcIdx+2];
                        validCount++;
                    }
                }
            }
            if(validCount>0){
                bSum/=validCount;
                gSum/=validCount;
                rSum/=validCount;
            }
            int destIdx=y*rowSize+x*3;
            data[destIdx]=(unsigned char)bSum;
            data[destIdx+1]=(unsigned char)gSum;
            data[destIdx+2]=(unsigned char)rSum;
        }
    }
    free(temp);
}

void writeBMP(const char* filename,unsigned char* data,BITMAP_FILE_HEADER fileHeader,BITMAP_INFO_HEADER infoHeader){
    FILE* fp=fopen(filename,"wb");
    if(!fp){
        printf("无法创建文件\n");
        return;
    }
    fileHeader.bfType=0x4D42;
    int rowSize=(infoHeader.biWidth*3+3)/4*4;
    fileHeader.bfSize=sizeof(BITMAP_FILE_HEADER)+sizeof(BITMAP_INFO_HEADER)+rowSize*infoHeader.biHeight;
    fileHeader.bfOffBits=sizeof(BITMAP_FILE_HEADER)+sizeof(BITMAP_INFO_HEADER);
    infoHeader.biSize=sizeof(BITMAP_INFO_HEADER);
    infoHeader.biBitCount=24;
    infoHeader.biCompression=0;
    infoHeader.biSizeImage=rowSize*infoHeader.biHeight;
    fwrite(&fileHeader,sizeof(BITMAP_FILE_HEADER),1,fp);
    fwrite(&infoHeader,sizeof(BITMAP_INFO_HEADER),1,fp);
    fwrite(data,rowSize*infoHeader.biHeight,1,fp);
    fclose(fp);
}

int main(int argc,char* argv[]){
    if(argc<4){
        printf("参数不足，请重新输入\n");
        return 1;
    }
    char* operation=argv[1];
    char* inputFile;
    if(strcmp(operation,"-g")==0)
	inputFile=argv[5];
    else
	inputFile=argv[2];
    char* outputFile=argv[argc-1];
    BITMAP_FILE_HEADER fileHeader={0};
    BITMAP_INFO_HEADER infoHeader={0};
    unsigned char* data=readBMP(inputFile,&fileHeader,&infoHeader);
    if(!data){
        printf("像素数据读取失败\n");
        return 1;
    }
    if(strcmp(operation,"-g")==0){
        if(argc<6){
            printf("灰度变换参数不足，请重新输入\n");
            free(data);
            return 1;
        }
        int method;
        if(strcmp(argv[2],"-l")==0)
        	method=1;
        else
        	method=2;
        double a_prime=atof(argv[3]);
        double b_prime=atof(argv[4]);
        grayTransform(data,infoHeader,method,a_prime,b_prime);
    }else if(strcmp(operation,"-h")==0){
        histogramEqualization(data,infoHeader);
    }else if(strcmp(operation,"-s")==0){
        if(argc<5){
            printf("图像平滑参数不足\n");
            free(data);
            return 1;
        }
        int windowSize=(strcmp(argv[3],"-3")==0)?3:5;
        imageSmoothing(data,infoHeader,windowSize);
    }else{
        printf("未知操作: %s\n",operation);
        free(data);
        return 1;
    }
    writeBMP(outputFile,data,fileHeader,infoHeader);
    free(data);
    return 0;
}